using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using Microsoft.Xna.Framework.Net;
using Microsoft.Xna.Framework.Storage;
using DSS_XNA.Engine.World;
using DSS_XNA.Engine.Script;
using DSS_XNA.Engine.Log;
using DSS_XNA.Engine.Error;
using DSS_XNA.Engine.Timer;
using DSS_XNA.Engine.Util;
using DSS_XNA.Engine.HUD;
using DSS_XNA.Engine.Primitives;
using DSS_XNA.Engine.Graphics;
using DSS_XNA.DeveloperWindow;
using DSS_XNA.Engine.Objects;
using DSS_XNA.Engine.System;
using DSS_XNA.Engine.Menu;

namespace DSS_XNA
{
    /// <summary>
    /// This is the main type for your game
    /// </summary>
    public class Game1 : Microsoft.Xna.Framework.Game
    {
        #region Properties

        public static bool IsToRestartGame
        {
            get { return Game1._isToRestartGame; }
            set { Game1._isToRestartGame = value; }
        }
        private static bool _isToRestartGame = false;

        public frmDeveloperWindow DeveloperWindow
        {
            get { return _developerWindow; }
            set { _developerWindow = value; }
        }
        private frmDeveloperWindow _developerWindow = null;

        public static bool IsDebugMode
        {
            get { return _isDebugMode; }
            set { _isDebugMode = value; }
        }
        private static bool _isDebugMode = false;

        public ObjectManager ObjectManager
        {
            get { return _objectManager; }
            set { _objectManager = value; }
        }
        private ObjectManager _objectManager = new ObjectManager();

        public static PrimitiveBatch PrimitiveBatch
        {
            get { return _primitiveBatch; }
            set { _primitiveBatch = value; }
        }
        private static PrimitiveBatch _primitiveBatch = null;

        public SpriteFont GameFont
        {
            get { return _gameFont; }
            set { _gameFont = value; }
        }
        private SpriteFont _gameFont = null;

        public string GameFontName
        {
            get { return _gameFontName; }
            set { _gameFontName = value; }
        }
        private string _gameFontName = String.Empty;

        public World CurrentWorld
        {
            get { return _currentWorld; }
            set { _currentWorld = value; }
        }
        private World _currentWorld = null;

        public static ScriptManager ScriptManager
        {
            get { return _scriptManager; }
            set { _scriptManager = value; }
        }
        private static ScriptManager _scriptManager = new ScriptManager();

        public FPSTimer FpsTimer
        {
            get { return _fpsTimer; }
            set { _fpsTimer = value; }
        }
        private FPSTimer _fpsTimer = new FPSTimer();

        public HUD Hud
        {
            get { return _hud; }
            set { _hud = value; }
        }
        private HUD _hud = null;

        public GameState State
        {
            get { return _state; }
            set { _state = value; }
        }
        private GameState _state = GameState.Menu;

        public Menu CurrentMenu
        {
            get { return _currentMenu; }
            set { _currentMenu = value; }
        }
        private Menu _currentMenu = null;

        /// <summary>
        /// This is used when the player presses ESC to exit the game. If a menu was loaded in the script,
        /// then we know that there is a menu avaliable. If so, when ESC is pressed, we load the menu, else we exit the game straight
        /// </summary>
        public bool IsMenuAvaliable
        {
            get { return _isMenuAvaliable; }
        }
        private bool _isMenuAvaliable = false;

        public KeyboardState? PrevKeyboardState
        {
            get { return _prevKeyboardState; }
            set { _prevKeyboardState = value; }
        }
        private KeyboardState? _prevKeyboardState = null;

        #endregion

        GraphicsDeviceManager graphics;
        SpriteBatch spriteBatch;

        public Game1()
        {
            graphics = new GraphicsDeviceManager(this);
            Content.RootDirectory = "Content";
        }

        /// <summary>
        /// Allows the game to perform any initialization it needs to before starting to run.
        /// This is where it can query for any required services and load any non-graphic
        /// related content.  Calling base.Initialize will enumerate through any components
        /// and initialize them as well.
        /// </summary>
        protected override void Initialize()
        {
            Logger.Log("\n\n**************** Program Started ****************", LogLevel.Minor);

            PrimitiveBatch = new PrimitiveBatch(this.GraphicsDevice);

            Logger.Log("Init ScriptManager", LogLevel.Minor);
            ScriptManager.Init(this);

            // run the default script to ensure things like the main world etc... are loaded
            ScriptManager.Run(this);

            base.Initialize();
        }

        /// <summary>
        /// LoadContent will be called once per game and is the place to load
        /// all of your content.
        /// </summary>
        protected override void LoadContent()
        {
            // Create a new SpriteBatch, which can be used to draw textures.
            spriteBatch = new SpriteBatch(GraphicsDevice);

            if (IsDebugMode)
            {
                Sprite.DebugBBTextire = this.Content.Load<Texture2D>("debug\\boundingBox");
            }
        }

        /// <summary>
        /// UnloadContent will be called once per game and is the place to unload
        /// all content.
        /// </summary>
        protected override void UnloadContent()
        {
            this.Content.Unload();
        }

        /// <summary>
        /// Allows the game to run logic such as updating the world,
        /// checking for collisions, gathering input, and playing audio.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Update(GameTime gameTime)
        {
            KeyboardState kb = Keyboard.GetState();
            MouseState ms = Mouse.GetState();
            GamePadState gs = GamePad.GetState(PlayerIndex.One);

            if (this.PrevKeyboardState != null && this.PrevKeyboardState.Value.IsKeyUp(Keys.Escape))
            {
                if (kb.IsKeyDown(Keys.Escape))
                {
                    if (this.IsMenuAvaliable && this.State != GameState.Menu)
                    {
                        ScriptManager.RunCommandRequest("LoadMenu");
                    }
                    else
                    {
                        this.Exit();
                    }
                }
            }

            if (this.State == GameState.Exiting)
                this.Exit();

            this.FpsTimer.Update(gameTime);

            this.SetWindowTitle();

            ScriptManager.Run(this);

            if (this.State == GameState.World)
            {
                if (this.Hud != null)
                    this.Hud.Update(this.CurrentWorld.CurrentWorld, gameTime, kb, ms, gs);

                this.CurrentWorld.Update(gameTime, kb, ms, gs);
            }
            else if(this.State == GameState.Menu)
            {
                this.CurrentMenu.Run(gameTime, kb, ms, gs);
            }
            else if (this.State == GameState.Paused)
            {
            }
            else if (this.State == GameState.Exiting)
            {
            }
            else
            {
                throw new DSSException(String.Format("Unknown game state: {0}", this.State));
            }

            base.Update(gameTime);

            this.PrevKeyboardState = kb;
        }

        protected override void Dispose(bool disposing)
        {
            base.Dispose(disposing);

            if (disposing)
            {
                PrimitiveBatch.Dispose();
            }
        }

        /// <summary>
        /// This is called when the game should draw itself.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Draw(GameTime gameTime)
        {
            GraphicsDevice.Clear(Color.CornflowerBlue);

            this.spriteBatch.Begin();

            if (this.State == GameState.World || this.State == GameState.Paused)
            {
                this.CurrentWorld.Render(this.spriteBatch);

                if (this.Hud != null)
                    this.Hud.Render(this.spriteBatch, this.GameFont);
            }
            else if (this.State == GameState.Menu)
            {
                this.CurrentMenu.Render(this.spriteBatch);
            }
            else if (this.State == GameState.Exiting)
            {
            }
            else
            {
                throw new DSSException(String.Format("Unknown game state: {0}", this.State));
            }

            this.spriteBatch.End();

            base.Draw(gameTime);
        }

        public void LoadWorld(string filePath)
        {
            if (this.CurrentWorld != null)
            {
                this.CurrentWorld.Destroy();
                this.CurrentWorld = null;
            }

            // load the default world
            string path = Util.GetFullPath(filePath);
            this.CurrentWorld = new World(path, this);

            this.State = GameState.World;

            this.Window.Title = this.CurrentWorld.Name;
        }

        public void LoadMenu(string filePath)
        {
            if (this.CurrentMenu != null)
            {
                this.CurrentMenu.Destroy();
                this.CurrentMenu = null;
            }

            // load the default world
            string path = Util.GetFullPath(filePath);
            this.CurrentMenu = new Menu(filePath, this);

            ScriptManager.SetNoScript();

            this.State = GameState.Menu;

            this._isMenuAvaliable = true;

            this.Window.Title = String.Format("Menu: {0}", this.CurrentMenu.Name);
        }

        public void SetScreenSize(int width, int height, bool fullscreen)
        {
            this.graphics.PreferredBackBufferWidth = width;
            this.graphics.PreferredBackBufferHeight = height;
            this.graphics.IsFullScreen = fullscreen;

            this.graphics.ApplyChanges();
        }

        protected override void OnExiting(object sender, EventArgs args)
        {
            base.OnExiting(sender, args);

            Game1.ScriptManager.Shutdown();
            Game1.ScriptManager = new ScriptManager();
        }

        private void SetWindowTitle()
        {
            if (IsDebugMode)
            {
                if (this.State == GameState.World || this.State == GameState.Paused)
                {
                    this.Window.Title = String.Format("{0} - {1}/{2} fps", this.CurrentWorld.Name, this.FpsTimer.CurrentFPS, 60);
                }
                else
                {
                    this.Window.Title = String.Format("{0} - {1}/{2} fps", this.CurrentMenu.Name, this.FpsTimer.CurrentFPS, 60);
                }
            }
            else
            {
                if (this.State == GameState.World || this.State == GameState.Paused)
                {
                    this.Window.Title = this.CurrentWorld.Name;
                }
                else
                {
                    this.Window.Title = this.CurrentMenu.Name;
                }
            }
        }
    }
}
